package com.pear.core.ctl;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * 类<code>DateTools</code>提供了日期操作的快捷功能。
 *
 * @author 顾晓健
 * @version %I%, %G%
 * @since 1.0
 */
public class DateTools {

    /**
     * 中文风格,如："1小时2分55秒"。
     */
    public static final int CHINESE_STYLE = 0;

    /**
     * 国际风格（暂不开放使用）。
     */
    private static final int I18N_STYLE = 1;

    /**
     * <p>判断两个日期是不是同一天。</p>
     *
     * @param date1 日期1
     * @param date2 日期2
     * @return 如果两个日期是同一天，则返回{@code true}；否则返回{@code false}
     */
    public static boolean isSameDay(Date date1, Date date2) {
        if (date1 == null || date2 == null) {
            throw new IllegalArgumentException("The date must not be null");
        }

        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);

        return isSameDay(cal1, cal2);
    }

    /**
     * <p>判断两个日期是不是同一天。</p>
     *
     * @param cal1 日期1
     * @param cal2 日期2
     * @return 如果两个日期是同一天，则返回{@code true}；否则返回{@code false}
     */
    public static boolean isSameDay(Calendar cal1, Calendar cal2) {
        if (cal1 == null || cal2 == null) {
            throw new IllegalArgumentException("The date must not be null");
        }

        return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR));
    }

    /**
     * <p>判断两个日期是不是同一刻。</p>
     *
     * @param date1 日期1
     * @param date2 日期2
     * @return 如果两个日期是同一刻，则返回{@code true}；否则返回{@code false}
     */
    public static boolean isSameInstant(Date date1, Date date2) {
        if (date1 == null || date2 == null) {
            throw new IllegalArgumentException("The date must not be null");
        }

        return date1.getTime() == date2.getTime();
    }

    /**
     * <p>判断两个日期是不是同一刻。</p>
     *
     * @param cal1 日期1
     * @param cal2 日期2
     * @return 如果两个日期是同一刻，则返回{@code true}；否则返回{@code false}
     */
    public static boolean isSameInstant(Calendar cal1, Calendar cal2) {
        if (cal1 == null || cal2 == null) {
            throw new IllegalArgumentException("The date must not be null");
        }

        return cal1.getTime().getTime() == cal2.getTime().getTime();
    }

    /**
     * <p>判断两个日期是不是同一本地时间。</p>
     *
     * @param cal1 日期1
     * @param cal2 日期2
     * @return 如果两个日期是同一本地时间，则返回{@code true}；否则返回{@code false}
     */
    public static boolean isSameLocalTime(Calendar cal1, Calendar cal2) {
        if (cal1 == null || cal2 == null) {
            throw new IllegalArgumentException("The date must not be null");
        }

        return (cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) &&
                cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) &&
                cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
                cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && cal1.getClass() == cal2.getClass());
    }

    /**
     * <p>为指定日期添加指定的年数，从而构建一个新日期。</p>
     *
     * @param date   指定日期
     * @param amount 指定的年数
     * @return 添加指定年数后的新日期
     */
    public static Date addYears(Date date, int amount) {
        return add(date, Calendar.YEAR, amount);
    }

    /**
     * <p>为指定日期添加指定的月份，从而构建一个新日期。</p>
     *
     * @param date   指定日期
     * @param amount 指定的月份
     * @return 添加指定月份后的新日期
     */
    public static Date addMonths(Date date, int amount) {
        return add(date, Calendar.MONTH, amount);
    }

    /**
     * <p>为指定日期添加指定的星期数，从而构建一个新日期。</p>
     *
     * @param date   指定日期
     * @param amount 指定的星期数
     * @return 添加指定星期数后的新日期
     */
    public static Date addWeeks(Date date, int amount) {
        return add(date, Calendar.WEEK_OF_YEAR, amount);
    }

    /**
     * <p>为指定日期添加指定的天数，从而构建一个新日期。</p>
     *
     * @param date   指定日期
     * @param amount 指定的天数
     * @return 添加指定天数后的新日期
     */
    public static Date addDays(Date date, int amount) {
        return add(date, Calendar.DAY_OF_MONTH, amount);
    }

    /**
     * <p>为指定日期添加指定的天数，从而构建一个新日期。</p>
     *
     * @param date   指定日期
     * @param amount 指定的天数
     * @return 添加指定天数后的新日期
     */
    public static Date addHours(Date date, int amount) {
        return add(date, Calendar.HOUR_OF_DAY, amount);
    }

    /**
     * <p>为指定日期添加指定的分钟数，从而构建一个新日期。</p>
     *
     * @param date   指定日期
     * @param amount 指定的分钟数
     * @return 添加指定分钟数后的新日期
     */
    public static Date addMinutes(Date date, int amount) {
        return add(date, Calendar.MINUTE, amount);
    }

    /**
     * <p>为指定日期添加指定的秒数，从而构建一个新日期。</p>
     *
     * @param date   指定日期
     * @param amount 指定的秒数
     * @return 添加指定秒数后的新日期
     */
    public static Date addSeconds(Date date, int amount) {
        return add(date, Calendar.SECOND, amount);
    }

    /**
     * <p>为指定日期添加指定的毫秒数，从而构建一个新日期。</p>
     *
     * @param date   指定日期
     * @param amount 指定的毫秒数
     * @return 添加指定毫秒数后的新日期
     */
    public static Date addMilliseconds(Date date, int amount) {
        return add(date, Calendar.MILLISECOND, amount);
    }

    /**
     * <p>为日期添加指定类型的数值，从而构建一个新日期。</p>
     *
     * @param date          指定日期
     * @param calendarField 指定类型的日期类型
     * @param amount        待添加的数值大小
     * @return 添加指定类型的数值的新日期
     */
    private static Date add(Date date, int calendarField, int amount) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }

        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(calendarField, amount);

        return c.getTime();
    }

    /**
     * <p>修改指定日期的年份，从而构建一个新日期。</p>
     *
     * @param date   指定日期
     * @param amount 指定的年份
     * @return 修改指定年份后的新日期
     */
    public static Date setYears(Date date, int amount) {
        return set(date, Calendar.YEAR, amount);
    }

    /**
     * <p>修改指定日期的月份，从而构建一个新日期。</p>
     *
     * @param date   指定日期
     * @param amount 指定的月份
     * @return 修改指定月份后的新日期
     */
    public static Date setMonths(Date date, int amount) {
        return set(date, Calendar.MONTH, amount);
    }

    /**
     * <p>修改指定日期的天数，从而构建一个新日期。</p>
     *
     * @param date   指定日期
     * @param amount 指定的天数
     * @return 修改指定天数后的新日期
     */
    public static Date setDays(Date date, int amount) {
        return set(date, Calendar.DAY_OF_MONTH, amount);
    }

    /**
     * <p>修改指定日期的小时数，从而构建一个新日期。</p>
     *
     * @param date   指定日期
     * @param amount 指定的小时数
     * @return 修改指定小时数后的新日期
     */
    public static Date setHours(Date date, int amount) {
        return set(date, Calendar.HOUR_OF_DAY, amount);
    }

    /**
     * <p>修改指定日期的分钟数，从而构建一个新日期。</p>
     *
     * @param date   指定日期
     * @param amount 指定的分钟数
     * @return 修改指定分钟数后的新日期
     */
    public static Date setMinutes(Date date, int amount) {
        return set(date, Calendar.MINUTE, amount);
    }

    /**
     * <p>修改指定日期的秒数，从而构建一个新日期。</p>
     *
     * @param date   指定日期
     * @param amount 指定的秒数
     * @return 修改指定秒数后的新日期
     */
    public static Date setSeconds(Date date, int amount) {
        return set(date, Calendar.SECOND, amount);
    }

    /**
     * <p>修改指定日期的毫秒数，从而构建一个新日期。</p>
     *
     * @param date   指定日期
     * @param amount 指定的毫秒数
     * @return 修改指定毫秒数后的新日期
     */
    public static Date setMilliseconds(Date date, int amount) {
        return set(date, Calendar.MILLISECOND, amount);
    }

    /**
     * <p>修改指定日期的指定类型的数值，从而构建一个新日期。</p>
     *
     * @param date          指定日期
     * @param calendarField 指定类型的日期类型
     * @param amount        待添加的数值大小
     * @return 修改指定类型的数值的新日期
     */
    private static Date set(Date date, int calendarField, int amount) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }

        Calendar c = Calendar.getInstance();
        c.setLenient(false);
        c.setTime(date);
        c.set(calendarField, amount);

        return c.getTime();
    }

    /**
     * <p>将Date日期类型转为Calendar日期类型。</p>
     *
     * @param date 日期
     * @return Calendar类型日期
     */
    public static Calendar toCalendar(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);

        return c;
    }

    /**
     * <p>获取两个日期之间的间隔时长,并返回其中文风格的字符表现形式。</p>
     *
     * @param date1 日期一
     * @param date2 日期二
     * @return 返回两个日期之间的间隔时长
     * @see DateTools#getDistanceTime(Date date1, Date date2, int style)
     */
    public static String getDistanceTime(Date date1, Date date2) {
        return getDistanceTime(date1, date2, CHINESE_STYLE);
    }

    /**
     * <p>获取两个日期之间的间隔时长,并返回其字符表现形式。</p>
     *
     * @param date1 日期一
     * @param date2 日期二
     * @param style 输出风格
     * @return 返回两个日期之间的间隔时长, 格式由style所指定的形式进行输出
     * @see DateTools#I18N_STYLE
     * @see DateTools#CHINESE_STYLE
     */
    public static String getDistanceTime(Date date1, Date date2, int style) {
        if (date1 == null || date2 == null) {
            throw new IllegalArgumentException("The date must not be null");
        }

        long day = 0;
        long hour = 0;
        long min = 0;
        long sec = 0;

        long time1 = date1.getTime();
        long time2 = date2.getTime();
        long diff;
        if (time1 < time2) {
            diff = time2 - time1;
        } else {
            diff = time1 - time2;
        }
        day = diff / (24 * 60 * 60 * 1000);
        hour = (diff / (60 * 60 * 1000) - day * 24);
        min = ((diff / (60 * 1000)) - day * 24 * 60 - hour * 60);
        sec = (diff / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);

        switch (style) {
            case CHINESE_STYLE:
                return formatTime2ChineseStyle(day, hour, min, sec);
            case I18N_STYLE:
                return formatTime2I18nStyle(day, hour, min, sec);
            default:
                return formatTime2ChineseStyle(day, hour, min, sec);
        }
    }

    /**
     * <p>将天、小时、分、秒按照中国日期表示方法进行格式化输出。</p>
     *
     * @param day  天
     * @param hour 小时
     * @param min  分
     * @param sec  秒
     * @return formatTime(0, 1, 2, 55)将输出："1小时2分55秒"
     */
    public static String formatTime2ChineseStyle(long day, long hour, long min, long sec) {
        StringBuilder sb = new StringBuilder();

        if (day != 0) {
            sb.append(day).append("天");
        }

        if (hour != 0) {
            sb.append(hour).append("小时");
        }

        if (min != 0) {
            sb.append(min).append("分");
        }

        sb.append(sec).append("秒");

        return sb.toString();
    }

    /**
     * <p>将天、小时、分、秒按照国际日期表示方法进行格式化输出。</p>
     *
     * @param day  天
     * @param hour 小时
     * @param min  分
     * @param sec  秒
     * @return null, 暂未实现
     */
    private static String formatTime2I18nStyle(long day, long hour, long min, long sec) {
        return null;
    }

    /**
     * <p>计算给定时间至当前时间的天数。</p>
     *
     * @param date 给定的时间
     * @return 给定时间至今的天数
     */
    public static long getDistanceDay2Now(String date) {
        if (StringTools.isEmpty(date)) {
            throw new IllegalArgumentException("The date must not be null");
        }

        DateFormat df = DateFormat.getDateInstance();
        Date d = null;

        try {
            d = df.parse(date);
        } catch (ParseException e) {
            throw new IllegalArgumentException("The date is not a date type");
        }

        return getDistanceDay2Now(d);
    }

    /**
     * <p>计算给定时间至当前时间的天数。</p>
     *
     * @param date 给定的时间
     * @return 给定时间至今的天数
     */
    public static long getDistanceDay2Now(Date date) {
        if (date == null) {
            throw new IllegalArgumentException("The date must not be null");
        }

        long day = 0;
        long old = date.getTime();
        long now = new Date().getTime();
        long secs = now - old;
        day = secs / (1000 * 24 * 60 * 60);

        return day;
    }

    /**
     * <p>得到当前年。</p>
     *
     * @return 返回当前年(YYYY)
     */
    public static int getCurrentYear() {
        return Calendar.getInstance().get(Calendar.YEAR);
    }

    /**
     * <p>得到当前月。</p>
     *
     * @return 返回当前月(1~12)
     */
    public static int getCurrentMonth() {
        return Calendar.getInstance().get(Calendar.MONTH) + 1;
    }

    /**
     * <p>得到当前日。</p>
     *
     * @return 返回当前日(1~31)
     */
    public static int getCurrentDay() {
        return Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
    }

    /**
     * <p>得到指定年月的天数。</p>
     *
     * @param month 指定月份
     * @param year  指定的年份
     * @return 天数
     */
    public static int getDayOfMonth(int month, int year) {
        int ret = 0;
        boolean flag = false;

        if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
            flag = true;
        }

        switch (month) {
            case 1:
                ret = 31;
                break;
            case 2:
                if (flag) {
                    ret = 29;
                } else {
                    ret = 28;
                }
                break;
            case 3:
                ret = 31;
                break;
            case 4:
                ret = 30;
                break;
            case 5:
                ret = 31;
                break;
            case 6:
                ret = 30;
                break;
            case 7:
                ret = 31;
                break;
            case 8:
                ret = 31;
                break;
            case 9:
                ret = 30;
                break;
            case 10:
                ret = 31;
                break;
            case 11:
                ret = 30;
                break;
            case 12:
                ret = 31;
                break;
            default:
                break;
        }

        return ret;
    }

    /**
     * <p>计算某天是星期几,如("2012-04-27")。</p>
     *
     * @param date 日期字符串
     * @return 星期
     */
    public static String getWeekDay(String date) {
        if (StringTools.isEmpty(date)) {
            throw new IllegalArgumentException("The date must not be null");
        }

        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        Calendar c = Calendar.getInstance();

        try {
            c.setTime(df.parse(date));
        } catch (ParseException e) {
            throw new IllegalArgumentException("The date is not a date type");
        }

        int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);
        switch (dayOfWeek) {
            case 1:
                return "星期日";
            case 2:
                return "星期一";
            case 3:
                return "星期二";
            case 4:
                return "星期三";
            case 5:
                return "星期四";
            case 6:
                return "星期五";
            case 7:
                return "星期六";
            default:
                date = "未匹配到";
        }

        return date;
    }

}
